
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.logging.Level;
import java.util.logging.Logger;


/**
 * Clase que implementa un panel donde se muestran las fichas del juego.
 * @author Matías Díaz
 * @author Sergio Flores.
 */
public class JTetravexPanel extends Panel implements MouseListener , MouseMotionListener
{

    private Color color;
    private Ficha fichas;
    private String tamano;
    private boolean isMove;
    private boolean dontMove;
    private Tablero cuadrados;
    private Tablero cuadradosAux;

    /**
     * Metodo constructor que inicializa los atributos de clase.
     */
    public JTetravexPanel ()
    {

        this.tamano = CommandName.TRES_TRES;
        this.fichas = new Ficha ( this.tamano );
        this.cuadrados = new Tablero ( this.tamano , new Point ( 25 , 25 ) , true );
        this.cuadradosAux = new Tablero ( this.tamano , new Point ( 700 , 25 ) , false );

        this.isMove = false;
        this.dontMove = false;
        this.setColor ();
        super.setBackground ( color );
        this.addMouseListener ( this );
        this.addMouseMotionListener ( this );
    }
    /**
     * Metodo que inicializa los atributos de clase pidiendo dos parametros.
     * @param tamano tamano que tendra el area de juego.
     * @param color  color de fondo
     */
    public JTetravexPanel ( String tamano , Color color )
    {

        this.tamano = tamano;
        this.fichas = new Ficha ( tamano );
        this.cuadrados = new Tablero ( tamano , new Point ( 25 , 25 ) , true );
        this.cuadradosAux = new Tablero ( tamano , new Point ( 700 , 25 ) , false );
        this.isMove = false;
        this.dontMove = false;
        this.color = color;
        super.setBackground ( color );
        this.addMouseListener ( this );
        this.addMouseMotionListener ( this );
    }

    /**
     * Metodo que setea el tamano de juego
     * @param tamano tamano de juego
     */
    public void setTamaño ( String tamano )
    {
        this.tamano = tamano;
        this.fichas = new Ficha ( tamano );
        this.cuadrados = new Tablero ( tamano , new Point ( 25 , 25 ) , true );
        this.cuadradosAux = new Tablero ( tamano , new Point ( 700 , 25 ) , false );
        repaint ();

    }
    /**
     * metodo que retorna el tamanao de juego.
     * @return  tamanano de juego.
     */
    public String getTamano ()
    {
        return tamano;
    }
    /**
     * Metodo que pintara las fichas en el area de juego.
     * @param g area grafica donde se pintaran las fichas.
     */
    @Override
    public void paint ( Graphics g )
    {
        Image bufferedImage = createImage ( this.getWidth () , this.getHeight () );
        Graphics2D buffer = ( Graphics2D ) bufferedImage.getGraphics ();

        buffer.setRenderingHint ( RenderingHints.KEY_COLOR_RENDERING , RenderingHints.VALUE_COLOR_RENDER_QUALITY );

        super.paintComponents ( buffer );
        if ( this.fichas != null )
        {
            this.fichas.paint ( buffer );
            this.cuadrados.paint ( buffer );
            this.cuadradosAux.paint ( buffer );

        }
        g.drawImage ( bufferedImage , 0 , 0 , this );
    }
    /**
     * Metodo que actualiza el area grafica
     * @param g area grafica a actualizar.
     */
    @Override
    public void update ( Graphics g )
    {
        paint ( g );
    }

    /**
     * Metodo que retorna el color de fondo
     * @return color de fondo
     */
    public Color getColor ()
    {

        return color;
    }
    /**
     * Metodo que setea el color de fondo por defecto.
     */
    private void setColor ()
    {
        this.color = Color.GRAY;
    }

    /**
     * Metodo que setea el color a uno especifico.
     * @param color color a setear.
     */
    public void setColor ( String color )
    {
        switch ( color )
        {

            case CommandName.COLOR_BLACK:
                this.color = Color.BLACK;
                break;
            case CommandName.COLOR_BLUE:
                this.color = Color.BLUE;
                break;
            case CommandName.COLOR_CYAN:
                this.color = Color.CYAN;
                break;
            case CommandName.COLOR_GRAY:
                this.color = Color.GRAY;
                break;
            case CommandName.COLOR_MAGENTA:
                this.color = Color.MAGENTA;
                break;
            case CommandName.COLOR_RED:
                this.color = Color.RED;
                break;
            case CommandName.COLOR_YELLOW:
                this.color = Color.YELLOW;
                break;
            case CommandName.COLOR_GREEN:
                this.color = Color.GREEN;
                break;
            default:
                this.color = Color.GRAY;
                break;
        }
        super.setBackground ( this.getColor () );
    }
    /**
     * Metodo que asigna la posicion a una ficha si esta dentro de un cuadrado
     * @param point  punto donde se encuentra la ficha.
     */
    public void asignaPosicion ( Point point )
    {



        if ( this.cuadrados.compruebaPunto ( point ) != null )
        {
            if ( this.isValide ( this.cuadrados.compruebaPunto ( point ) ) && this.noHayFicha ( point ) )
            {
                this.fichas.getFichaAux ().setPuntoInicial ( this.cuadrados.compruebaPunto ( point ) );
                this.fichas.getFichaAux ().setEstaEnTablero ( true );

            }
            else
            {
                this.fichas.getFichaAux ().devolver ();


            }
        }
        if ( this.cuadradosAux.compruebaPunto ( point ) != null )
        {

            if ( this.noHayFicha ( point ) )
            {
                this.fichas.getFichaAux ().setPuntoInicial ( this.cuadradosAux.compruebaPunto ( point ) );
                this.fichas.getFichaAux ().setEstaEnTablero ( false );
            }
            else
            {
                this.fichas.getFichaAux ().devolver ();

            }
        }
        else
        {
            this.fichas.getFichaAux ().devolver ();

        }
        repaint ();

        if ( this.gameOver () )
        {
            this.stopCronometro ();
            this.newPuntaje ();
            this.dontMove = true;
        }
        

    }
    /**
     * Metodo que checkea que el juego este finalizado.
     * @return  true o false si el juego termino  o no.
     */
    private boolean gameOver ()
    {
        
        repaint ();
        for ( int i = 0 ; i < this.fichas.getFichas ().size () ; i++ )
        {
            if ( !fichas.getFichas ().get ( i ).getEstaEnTablero () )
            {

                return false;
            }

        }

        return true;
    }
    /**
     * metodo que checkea si un movimiento es valido segun sus numeros
     * @param point punto donde esta la ficha
     * @return  true o false si es valido o no.
     */
    private boolean isValide ( Point point )
    {
        for ( JTriangulo trian : this.fichas.getFichas () )
        {
            Point punto = trian.getPuntoInicial ();

            if ( point.x - 50 >= ( punto.x ) && point.y >= ( punto.y ) && point.x - 50 < ( punto.x + 100 ) && point.y < ( punto.y + 100 ) )
            {
                if ( trian.getNumeroRight () != this.fichas.getFichaAux ().getNumeroLeft () && !trian.equals ( this.fichas.getFichaAux () ) )
                {
                    return false;
                }
            }
            if ( point.x + 150 >= punto.x && point.y >= punto.y && point.x + 150 < ( punto.x + 100 ) && point.y < ( punto.y + 100 ) )
            {
                if ( trian.getNumeroLeft () != this.fichas.getFichaAux ().getNumeroRight () && !trian.equals ( this.fichas.getFichaAux () ) )
                {
                    return false;
                }
            }

            if ( point.x >= punto.x && point.y + 150 >= punto.y && point.x < ( punto.x + 100 ) && point.y + 150 < ( punto.y + 100 ) )
            {
                if ( trian.getNumeroUp () != this.fichas.getFichaAux ().getNumeroDown () && !trian.equals ( this.fichas.getFichaAux () ) )
                {
                    return false;
                }
            }
            if ( point.x >= punto.x && point.y - 50 >= punto.y && point.x < ( punto.x + 100 ) && point.y - 50 < ( punto.y + 100 ) )
            {
                if ( trian.getNumeroDown () != this.fichas.getFichaAux ().getNumeroUp () && !trian.equals ( this.fichas.getFichaAux () ) )
                {
                    return false;
                }
            }

        }
        return true;

    }
    /**
     * Metodo que checke si un cuadrado tiene ficha o no
     * @param point punto del cuadrado
     * @return true o false si hay o no ficha.
     */
    private boolean noHayFicha ( Point point )
    {
        for ( JTriangulo trian : this.fichas.getFichas () )
        {
            Point punto = trian.getPuntoInicial ();
            if ( point.x >= punto.x && point.y >= punto.y && point.x < ( punto.x + 100 ) && point.y < ( punto.y + 100 ) )
            {
                return false;
            }
        }
        return true;
    }
    /**
     * Metodo que muestra la solucion en pantalla.
     */
    public void mostrarSolucion ()
    {
        this.fichas.mostrarSolucion ();
        this.stopCronometro ();

        this.dontMove = true;
        repaint ();

    }
    /**
     * Metodo que muestra la ventana de nuevos puntajes.
     */
    public void newPuntaje ()
    {
        JTetratexFrame frame = ( JTetratexFrame ) this.getParent ().getParent ().getParent ().getParent ();
        frame.showNewPuntaje ();
        frame.juegoTerminado();
        
    }
    /**
     * Metodo que para el cronometro.
     */
    public void stopCronometro ()
    {
        JTetratexFrame frame = ( JTetratexFrame ) this.getParent ().getParent ().getParent ().getParent ();
        frame.stopCronometro ();

    }
    /**
     * Metodo que realiza acciones cuando el mouse es clickeado.
     * @param e actionlistener que llamo al metodo.
     */
    @Override
    public void mouseClicked ( MouseEvent e )
    {
    }
    /**
     * Metodo que realiza acciones cuando el mouse es presionado
     * @param e actionlistener que llamo al metodo.
     */
    @Override
    public void mousePressed ( MouseEvent e )
    {
        this.isMove = this.fichas.estaFichaMovere ( e.getPoint () );

    }
    /**
     * Metodo que realiza acciones cuando el mouse es liberado.
     * @param e actionlistener que llamo al metodo.
     */
    @Override
    public void mouseReleased ( MouseEvent e )
    {
        this.isMove = false;
        if ( this.dontMove == false )
        {
            asignaPosicion ( e.getPoint () );
        }
        repaint ();
    }
    /**
     * Metodo que realiza acciones cuando el mouse esta  dentro de la pantalla.
     * @param e actionlistener que llamo al metodo
     */
    @Override
    public void mouseEntered ( MouseEvent e )
    {
    }
    /**
     * Metodo que realiza acciones cuando el mouse es estimulado.
     * @param e actionlistener que llamo al metodo
     */
    @Override
    public void mouseExited ( MouseEvent e )
    {
    }
    /**
     * Metodo que realiza acciones cuando el mouse es arrastrado.
     * @param e actionlistener que llamo al metodo.
     */
    @Override
    public void mouseDragged ( MouseEvent e )
    {
        if ( isMove && dontMove == false )
        {
            Point point = e.getPoint ();
            point.x -= 50;
            point.y -= 50;
            if ( point.x < this.getWidth () - 100 && point.y < this.getHeight () - 100 && point.x > 0 && point.y > 0 )//para que no se salga del panel
            {
                this.fichas.moveFicha ( point );
                repaint ();
            }
        }

    }
    
    /**
     * Metodo que realiza acciones cuando el mouse es movido.
     * @param e actionlistener que llamo al metodo
     */
    @Override
    public void mouseMoved ( MouseEvent e )
    {
    }
}
